﻿#include "serial_dev.h"

serial_dev::serial_dev(QObject *parent) : QObject(parent), open_flag(false)
{
    //初始化qserialport
    drv = std::make_unique<QSerialPort>();
    inner_timer = std::make_unique<QTimer>();
    buf = std::make_shared<std::queue<uint8_t>>();
    drv->setDataBits(QSerialPort::Data8);
    drv->setParity(QSerialPort::NoParity);
    drv->setStopBits(QSerialPort::OneStop);
    drv->setFlowControl(QSerialPort::NoFlowControl);
    drv->setReadBufferSize(4096*10);
    connect(drv.get(),SIGNAL(readyRead()),this,SLOT(com_rev()));
    connect(inner_timer.get(), SIGNAL(timeout()), this, SLOT(timeout_handle()));

    parse_thread = std::thread(&serial_dev::parse_task, this);
    parse_thread.detach();
}


serial_dev::~serial_dev()
{
    if(open_flag && drv)
    {
        drv->close();
    }
}

int serial_dev::com_open(const QString& portname,const int baudrate)
{
    if(portname.isEmpty() || baudrate == NULL)
    {
        qDebug()<<"param is empty";
        return -1;
    }

    drv->setPortName(portname);
    drv->setBaudRate(baudrate);
    if(true == drv->open(QIODevice::ReadWrite))
    {
        open_flag = true;
        return 0;
    }
    else
    {
        open_flag = false;
        drv->close();
        qDebug()<<"com open failed";
        return -1;
    }

}

int serial_dev::com_close()
{

    if(!open_flag)
    {
        qDebug()<<"serial already close";
        goto already_close;
    }
    drv->close();
    open_flag = false;
    return 0;

already_close:
    return -1;

}

bool serial_dev::status()
{
    return open_flag;
}

QStringList serial_dev:: refresh_port()
{
    QStringList port_info;


    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {
        port_info << info.portName();
    }

    return port_info;
}


void serial_dev::com_rev()
{

    inner_timer->start(5);

}

void serial_dev::timeout_handle()
{
    inner_timer->stop();

    QByteArray tmp = drv->readAll();

    std::unique_lock<std::mutex> lock(mtx);

    for (auto var : tmp)
    {
        buf->push(var);
    }
    cv.notify_all();
}

void serial_dev::set_store_flag(bool flag)
{
    if (flag == true)
    {
        rawdata_store_flag = true;
    }
    else
    {
        rawdata_store_flag = false;
    }
}

void serial_dev::parse_task()
{
    int ret = -1;
    uint8_t mode = 0;
    uint16_t devaddr = 0;
    while (1)
    {   
        std::unique_lock<std::mutex> lock(mtx);

        while (buf->empty())
        {
            cv.wait(lock);
        }

        while (!buf->empty())
        {
            auto var = buf->front();
            buf->pop();
            if (rawdata_store_flag)
            {
                rawdata_p += var;
                if (rawdata_p.count() == 1024)
                {
                    FILE* fd = fopen("./raw.bin", "ab+");
                    fwrite(rawdata_p.data(), rawdata_p.count(), 1, fd);
                    fclose(fd);
                    rawdata_p.clear();
                }
            }
            ret = parse_process(var, &mode, &devaddr);
            if (ret == 0)
            {
                if (mode == DATA_TYPE_PINPU)
                {
                    emit get_pingpu_data(&revdata_pinpu, &devaddr);
                    //qDebug() << "size is:" << revdata_pinpu.size();
                }
                else if(mode == DATA_TYPE_RANGE)
                {
                    emit get_range_data(&revdata_range);
                    //qDebug() << "size is:" << revdata_range.size();
                }
                else if (mode == DATA_TYPE_RAW)
                {
                    emit get_raw_data(&revdata_pinpu, &devaddr);
                }
                else if (mode == DATA_TYPE_SPEED)
                {
                    emit get_speed_data(&revdata_range);
                }
            }

        }

    }
}

int serial_dev::parse_process(uint8_t data, uint8_t *output_mode, uint16_t *output_devaddr)
{
    static uint8_t stat;
    static uint16_t count;
    static uint16_t sum;
    static uint8_t fuccode_store;
    static uint16_t length_store;
    static uint16_t devaddr_store;  
    static uint16_t checksum_store;
    static uint8_t error_flag = 0;

    switch (stat)
    {
    case DATA_CHECK_HEAD:

        sum += data;

        if (!count)
        {
            if (data == DATA_HEAD_H)
            {
                count++;
            }
            else
            {
                count = 0;
                sum = 0;
            }
        }
        else if(count == 1)
        {
            if (data == DATA_HEAD_L)
            {
                count = 0;
                stat = DATA_CHECK_LENGTH;
            }
            else
            {
                count = 0;
                sum = 0;
            }
        }
        else
        {
            error_flag = 1;
            break;
        }

        break;
    case DATA_CHECK_LENGTH:

        sum += data;

        if (!count)
        {
            count++;
            length_store |= (data<<8);
        }
        else if(count == 1)
        {
            count = 0;
            length_store |= data;
            if (length_store >= 1 && length_store <= (SIZE_DATA + SIZE_RES + SIZE_PACKTOTAL + SIZE_PACKID + SIZE_FUCCODE))
            {
                stat = DATA_CHECK_DEVADDR;
            }
            else
            {
                stat = DATA_CHECK_HEAD;
                length_store = 0;
                sum = 0;
            }
        }
        else
        {
            error_flag = 1;
            break;
        }

        break;
    case DATA_CHECK_DEVADDR:

        sum += data;

        if (!count)
        {
            count++;
            devaddr_store |= (data << 8);
        }
        else if(count == 1)
        {
            count = 0;
            devaddr_store |= data;
            stat = DATA_CHECK_FUCCODE;
        }
        else
        {
            error_flag = 1;
            break;
        }

        break;
    case DATA_CHECK_FUCCODE:

        sum += data;

        fuccode_store = data;
        if (length_store == 1)
        {
            stat = DATA_CHECK_CHECKSUM;
        }
        else
        {
            stat = DATA_CHECK_DATA;
        }
        break;
    case DATA_CHECK_DATA:

        sum += data;

        if (fuccode_store == DATA_FUNCODE_RANGE || fuccode_store == DATA_FUNCODE_SPEED)
        {
            count++;
            tempdata_range += data;
        }
        else if (fuccode_store == DATA_FUNCODE_FFT)
        {
            count++;
            tempdata_pinpu += data;
        }

        if (count == (length_store - 1))    //length_store without fuccode size
        {
            count = 0;
            stat = DATA_CHECK_CHECKSUM;
        }

        break;
    case DATA_CHECK_CHECKSUM:

        if (!count)
        {
            count++;
            checksum_store |= (data << 8);
        }
        else if(count == 1)
        {
            count = 0;
            checksum_store |= data;
            if (checksum_store == sum)
            {
                stat = DATA_CHECK_TAILER;
            }
            else
            {
                error_flag = 1;
                break;
            }
        }
        else
        {
            error_flag = 1;
            break;
        }
        
        break;

    case DATA_CHECK_TAILER:

        if (!count)
        {
            if (data == DATA_TAIL_H)
            {
                count++;
            }
            else
            {
                error_flag = 1;
                break;
            }
        }
        else if(count == 1)
        {
            if (data == DATA_TAIL_L)
            {
                uint8_t temp = fuccode_store;
                *output_devaddr = devaddr_store;
                count = 0;
                fuccode_store = 0;
                length_store = 0;
                checksum_store = 0;
                devaddr_store = 0;
                stat = DATA_CHECK_HEAD;
                sum = 0;
                return parse_merge_pack(temp, output_mode);
            }
            else
            {
                error_flag = 1;
                break;
            }
        }
        else
        {
            error_flag = 1;
            break;
        }

        break;
    }

    if (error_flag)
    {
        count = 0;
        fuccode_store = 0;
        length_store = 0;
        checksum_store = 0;
        devaddr_store = 0;
        stat = DATA_CHECK_HEAD;
        error_flag = 0;
        sum = 0;
        tempdata_pinpu.clear();
        tempdata_range.clear();
    }


    return -1;
}

int serial_dev::parse_merge_pack(uint16_t funcode, uint8_t *output_mode)
{
    static uint8_t stat;
    static uint16_t last_id;

    if (funcode == DATA_FUNCODE_RANGE)
    {
        revdata_range.clear();
        revdata_range += tempdata_range;
        tempdata_range.clear();
        *output_mode = DATA_TYPE_RANGE;
        return 0;
    }
    else if(funcode == DATA_FUNCODE_SPEED)
    {
        revdata_range.clear();
        revdata_range += tempdata_range;
        tempdata_range.clear();
        *output_mode = DATA_TYPE_SPEED;
        return 0;
    }
    else if (funcode == DATA_FUNCODE_FFT || funcode == DATA_FUNCODE_RAW)
    {
        QByteArray packtotal_t = tempdata_pinpu.mid(0, 2);
        QByteArray packid_t = tempdata_pinpu.mid(2, 2);
        uint16_t packtotal = (static_cast<uint8_t>(packtotal_t.at(1)) << 8) | static_cast<uint8_t>(packtotal_t.at(0));
        uint16_t packid = (static_cast<uint8_t>(packid_t.at(1)) << 8) | static_cast<uint8_t>(packid_t.at(0));
        
        revdata_pinpu_t += tempdata_pinpu.mid(4,SIZE_DATA);
        tempdata_pinpu.clear();

        if (packtotal > 1)
        {
            switch (stat)
            {
            case 0:
                if (packid == 0)
                {
                    last_id = packid;
                    stat = 1;
                }
                else
                {
                    revdata_pinpu_t.clear();
                }
                break;
            case 1:
                if (last_id == packid - 1)
                {
                    last_id = packid;
                    if (packid == (packtotal - 1))
                    {
                        stat = 0;
                        revdata_pinpu.clear();
                        revdata_pinpu = revdata_pinpu_t;
                        revdata_pinpu_t.clear();
                        if (funcode == DATA_FUNCODE_FFT)
                        {
                            *output_mode = DATA_TYPE_PINPU;
                        }
                        else if (funcode == DATA_FUNCODE_RAW)
                        {
                            *output_mode = DATA_TYPE_RAW;
                        }
                        return 0;
                    }
                }
                else
                {
                    revdata_pinpu_t.clear();
                    stat = 0;
                }
            }
        }
        else if(packtotal == 1)
        {
            revdata_pinpu.clear();
            revdata_pinpu = revdata_pinpu_t;
            revdata_pinpu_t.clear();
            if (funcode == DATA_FUNCODE_FFT)
            {
                *output_mode = DATA_TYPE_PINPU;
            }
            else if (funcode == DATA_FUNCODE_RAW)
            {
                *output_mode = DATA_TYPE_RAW;
            }
            return 0;
        }

    }

    return -1;
}